﻿using System;
using System.Collections.Generic;
using System.Reflection;
using UnityEditor;
using UnityEngine;

namespace PerfAssist.Common.Editor.TableView
{
    public delegate void SelectionHandler(object selected, int col);

    public partial class TableView : IDisposable
    {
        // show internal sequential ID in the first column
        public bool ShowInternalSeqID = false;

        public TableView(EditorWindow hostWindow, Type itemType)
        {
            m_hostWindow = hostWindow;
            m_itemType = itemType;
        }

        public TableViewAppr Appearance {
            get { return new TableViewAppr();}
        }

        public void Dispose()
        {
        }

        public event SelectionHandler OnSelected;

        public void ClearColumns()
        {
            m_descArray.Clear();
        }

        public bool AddColumn(string colDataPropertyName, string colTitleText, float widthByPercent,
            TextAnchor alignment = TextAnchor.MiddleCenter, string fmt = "")
        {
            var desc = new TableViewColDesc();
            desc.PropertyName = colDataPropertyName;
            desc.TitleText = colTitleText;
            desc.Alignment = alignment;
            desc.WidthInPercent = widthByPercent;
            desc.Format = string.IsNullOrEmpty(fmt) ? null : fmt;
            desc.MemInfo = m_itemType.GetField(desc.PropertyName,
                BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetField);
            if (desc.MemInfo == null)
            {
                desc.MemInfo = m_itemType.GetProperty(desc.PropertyName,
                    BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance | BindingFlags.GetProperty);
                if (desc.MemInfo == null)
                {
                    Debug.LogWarningFormat("Field '{0}' accessing failed.", desc.PropertyName);
                    return false;
                }
            }

            m_descArray.Add(desc);
            return true;
        }

        public void RefreshData(List<object> entries, Dictionary<object, Color> specialTextColors = null)
        {
            m_lines.Clear();

            if (entries != null && entries.Count > 0)
            {
                m_lines.AddRange(entries);

                SortData();
            }

            m_specialTextColors = specialTextColors;
        }

        public void Draw(Rect area)
        {
            GUILayout.BeginArea(area);
            _scrollPos = GUILayout.BeginScrollView(_scrollPos, GUIStyle.none, GUI.skin.verticalScrollbar);
            //Debug.LogFormat("scroll pos: {0:0.00}, {1:0.00}", _scrollPos.x, _scrollPos.y);
            {
                var s = new GUIStyle();
                s.fixedHeight = Appearance.LineHeight * (m_lines.Count + 1);
                s.stretchWidth = true;
                var r = EditorGUILayout.BeginVertical(s);
                {
                    // this silly line (empty label) is required by Unity to ensure the scroll bar appear as expected.
                    PAEditorUtil.DrawLabel("", Appearance.Style_Line);

                    DrawTitle(r.width);

                    // these first/last calculatings are for smart clipping 
                    var firstLine = Mathf.Max((int) (_scrollPos.y / Appearance.LineHeight) - 1, 0);
                    var shownLineCount = (int) (area.height / Appearance.LineHeight) + 2;
                    var lastLine = Mathf.Min(firstLine + shownLineCount, m_lines.Count);

                    for (var i = firstLine; i < lastLine; i++) DrawLine(i + 1, m_lines[i], r.width);
                }
                EditorGUILayout.EndVertical();
            }
            GUILayout.EndScrollView();
            GUILayout.EndArea();
        }

        public void SetSortParams(int sortSlot, bool descending)
        {
            _sortSlot = sortSlot;
            Descending = descending;
        }

        public void SetSelected(object obj)
        {
            m_selected = obj;

            if (OnSelected != null)
                OnSelected(obj, 0);
        }

        public object GetSelected()
        {
            return m_selected;
        }
    }
}